#
#  Copyright 2022 The Open Islands Authors. All Rights Reserved.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#
import requests

from pyoi.context.mq import MQ
from pyoi.context.types import FederatedPacketRouting, FederatedPacketHeaders, FederatedPacketProperties
from pyoi.runtime.base_runtime import BaseRuntime
from pyoi.runtime.types import MessageCode
from pyoi.runtime_env import RuntimeENV
from pyoi.util.base_utils import current_timestamp, trys
from pyoi.util.log_utils import getLogger

LOGGER = getLogger()


class FederatedSender(BaseRuntime):
    def run(self):
        service_addresses = self.runtime_config.serviceAddresses
        service_params = self.runtime_config.serviceParams
        mq_args, mq_manager = MQ.create(
            group=RuntimeENV.TASK_ID,
            address=service_addresses.get("mq", {}),
            params=service_params.get("mq", {}))
        channel = MQ.get_channel(mq_args=mq_args,
                                 routing_info=FederatedPacketRouting(task_id=RuntimeENV.TASK_ID))
        federated_send_packet_topic = f"send-{RuntimeENV.TASK_ID}-{RuntimeENV.ROLE}-{RuntimeENV.NODE_ID}"
        mq_manager.create_topic(federated_send_packet_topic)
        LOGGER.info(f"start consume on topic {federated_send_packet_topic}")
        for deliver, headers, data in channel.consume_on(federated_send_packet_topic):
            properties: FederatedPacketProperties = headers.properties
            LOGGER.info(
                f"receive packet from queue, start to send name: {properties.name}, tag: {properties.tag}")
            st = current_timestamp()
            response = self.forwarding_federation_data(
                proxy_config=service_addresses.get("proxy"),
                headers=headers,
                data=data)
            et = current_timestamp()
            log_msg = f"send name: {properties.name}, tag: {properties.tag}"
            LOGGER.debug(
                f"{log_msg}, response: {response}, elapsed: {et - st}")
            if MessageCode.if_success(response):
                channel.ack(deliver=deliver)
            else:
                raise Exception(f"{log_msg} failed by some tries, exit task sender now, please check log")

    @trys(times=5, interval=5, set_retries=True)
    def forwarding_federation_data(self, proxy_config, headers: FederatedPacketHeaders, data: bytes, retries=0):
        # todo: support grpc
        routing_info = headers.routing
        if retries > 0:
            routing_info.retries = retries
        if len(data) > 1024:
            headers.encoding_request_content = "gzip"
        endpoint = f"/runtime/v1/transmission/{routing_info.dest_node_id}/{routing_info.dest_role}/{RuntimeENV.TASK_ID}/remote"
        federation_remote_url = f"http://{proxy_config['host']}:{proxy_config['port']}{endpoint}"
        response = requests.post(federation_remote_url, data=data, headers=headers.to_dict())
        if response.status_code not in {200, 201}:
            raise Exception(
                f"send federation data error, url: {federation_remote_url}, response code: {response.status_code}, response text: {response.text}")
        response_dict = response.json()
        if not MessageCode.if_success(response_dict):
            raise Exception(f"send federation data error, url: {federation_remote_url}, response: {response_dict}")
        return response_dict

    def handle_exception(self):
        pass

    def finally_do(self):
        pass


if __name__ == '__main__':
    FederatedSender().start()
